package msu.ml.presentation;

import weka.core.*;
import java.io.*;
import javax.imageio.*;
import java.awt.image.*;
import java.awt.*;

public class SilhouetteCoefficient
{
   public static double getForClustering(double [] s)
   {
      if(s.length == 0)
         return 0.0;

      double sum = 0;
      for(int i = 0; i < s.length; i++)
            sum += s[i];
      return sum / (double)s.length;
   }

   public static double [] getForClusters(double [] s, int [] clusters, int numClusters)
   {
      double [] result = new double[numClusters];

      for(int i = 0; i < numClusters; i++)
      {
         double sum = 0;
         int count = 0;
         for(int j = 0; j < s.length; j++)
         {
            if(clusters[j] == i)
            {
               sum += s[j];
               count++;
            }
         }

         result[i] = (count == 0) ? 0 : sum / (double)count;
      }

      return result;
   }

   public static double [] getForClusters(Instances data, int [] clusters, int numClusters)
   {
      double [] s = getForInstances(data, clusters, numClusters);
      return SilhouetteCoefficient.getForClusters(s, clusters, numClusters);
   }

   public static double [] getForInstances(Instances data, int [] clusters, int numClusters)
   {
      EuclideanDistance eu = new EuclideanDistance(data);

      double [] a = new double[clusters.length];
      for(int i = 0; i < clusters.length; i++)
      {
         Instance inst = data.instance(i);
         double sum = 0;
         int count = 0;
         for(int j = 0; j < clusters.length; j++)
         {
            if(j != i && clusters[i] == clusters[j])
            {
               sum += eu.distance(inst, data.instance(j));
               count++;
            }
         }
        
         a[i] = (count == 0) ? 0 : sum / (double)count;
      }

      double [] b = new double[clusters.length];
      for(int i = 0; i < clusters.length; i++)
      {
         Instance inst = data.instance(i);

         double minB = Double.MAX_VALUE;

         for(int j = 0; j < numClusters; j++)
         {

            if(clusters[i] != j)
            {
               double sum = 0;
               int count = 0;
               for(int k = 0; k < clusters.length; k++)
               {
                  if(clusters[k] == j)
                  {
                     sum += eu.distance(inst, data.instance(k));
                     count++;
                  }
               }
               double tempB = (count == 0) ? 0 : sum / (double)count;
               if(tempB < minB)
                  minB = tempB;
            }
         }
         b[i] = minB;
      }
      
      double [] s = new double[clusters.length];
      for(int i = 0; i < clusters.length; i++)
      {
         double max = Math.max(a[i], b[i]);
         s[i] = (max == 0) ? 0 : (b[i] - a[i]) / max;
      }

      return s;
   }

   public static void saveImage(Instances data, double [] sCoeff, String file, 
      int width, int height)
   {
      try
      {
         int [] x = new int[sCoeff.length];
         int [] y = new int[sCoeff.length];

         BufferedImage image = new BufferedImage(width, 
               height, BufferedImage.TYPE_INT_ARGB);
         Graphics2D g = image.createGraphics();
         g.setColor(Color.white);
         g.fillRect(0,0,width,height);
         for(int i = 0; i < data.numInstances(); i++)
         {
            Instance inst = data.instance(i);
            x[i] = (int)inst.value(0) + (width / 2);
            y[i] = height - ((int)inst.value(1) + (height / 2));
         }

         for(int i = 0; i < x.length; i++)
         {
            float grad = ((float)sCoeff[i] + 1.0f) / 2.0f;
            g.setColor(new Color(grad, grad, grad));
            g.fillOval(x[i], y[i], 2, 2);
         }

         File imageFile = new File(file);
         ImageIO.write(image, "png", imageFile);
      }
      catch(Exception e)
      {
         System.out.println("Exception while saving image");
         System.out.println(e.getMessage());
         e.printStackTrace();
      }


   }
}
